#pragma once

#include "TypedProperty.h"

namespace My {

/**
 * Accesses value of edited object 
 */
template <typename C>
class ObjectAccess : public virtual Access {
public:
	C & Cast(EditableObject & obj) const {
		assert(dynamic_cast<C *>(&obj) != 0);
		return dynamic_cast<C &>(obj);
	}
	const C & Cast(const EditableObject & obj) const {
		assert(dynamic_cast<const C *>(&obj) != 0);
		return dynamic_cast<const C &>(obj);
	}
public:
	virtual bool Accept(const EditableObject & obj) const {
		return (dynamic_cast<const C *>(&obj) != 0);
	}
};

/**
 * Accesses value of another property
 */
template <typename S>
class PropertyAccess : public virtual Access {
public:
	PropertyAccess(BasicProperty<S> & owner)
		: m_Owner(owner) { }
public:
	virtual bool Accept(const EditableObject & obj) const
		{ return m_Owner.Accept(obj); }
protected:
	BasicProperty<S> &	m_Owner;
};

// ObjectAccess subclasses

template <typename C, typename T>
class GetObjectValueFunction : public ReadAccess<T>, public ObjectAccess<C> {
public:
	GetObjectValueFunction(T (C::* ptr)() const)
		: m_pGet(ptr) { }
public:
	virtual bool GetValue(const EditableObject & obj, T & value) const {
		if (m_pGet == 0)
			return false;
		value = (Cast(obj).* m_pGet)();
		return true;
	}
protected:
	T (C::*	m_pGet)() const;
};

template <typename C, typename T>
class SetObjectValueFunction : public WriteAccess<T>, public ObjectAccess<C> {
public:
	SetObjectValueFunction(void (C::* ptr)(T))
		: m_pSet(ptr) { }
public:
	virtual void SetValue(EditableObject & obj, const T & value) const {
		if (m_pSet == 0)
			return;
		(Cast(obj).* m_pSet)(value);
	}
protected:
	void (C::*	m_pSet)(T);
};

template <typename C, typename T>
class GetObjectRefFunction : public ReadAccess<T>, public ObjectAccess<C> {
public:
	GetObjectRefFunction(const T & (C::* ptr)() const)
		: m_pGet(ptr) { }
public:
	virtual bool GetValue(const EditableObject & obj, T & value) const {
		if (m_pGet == 0)
			return false;
		value = (Cast(obj).* m_pGet)();
		return true;
	}
protected:
	const T & (C::*	m_pGet)() const;
};

template <typename C, typename T>
class SetObjectRefFunction : public WriteAccess<T>, public ObjectAccess<C> {
public:
	SetObjectRefFunction(void (C::* ptr)(const T &))
		: m_pSet(ptr) { }
public:
	virtual void SetValue(EditableObject & obj, const T & value) const {
		if (m_pSet == 0)
			return;
		(Cast(obj).* m_pSet)(value);
	}
protected:
	void (C::*	m_pSet)(const T &);
};

template <typename C, typename T>
class ObjectValue : public ReadWriteAccess<T>, public ObjectAccess<C> {
public:
	ObjectValue(T (C::* ptr))
		: m_pValue(ptr) { }
public:
	virtual bool GetValue(const EditableObject & obj, T & value) const {
		if (m_pValue == 0)
			return false;
		value = Cast(obj).* m_pValue;
		return true;
	}
	virtual void SetValue(EditableObject & obj, const T & value) const {
		if (m_pValue == 0)
			return;
		Cast(obj).* m_pValue = value;
	}
	virtual bool ReadOnly() const {
		return m_pValue == 0; 
	}
protected:
	T	(C::* m_pValue);
};

// PropertyAccess subclasses

template <typename S, typename T>
class GetPropertyValueFunction : public ReadAccess<T>, public PropertyAccess<S> {
public:
	GetPropertyValueFunction(BasicProperty<S> & owner, T (S::* ptr)() const)
		: PropertyAccess<S>(owner), m_pGet(ptr) { }
public:
	virtual bool GetValue(const EditableObject & obj, T & value) const {
		if (m_pGet == 0)
			return false;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return false;
		value = (tmp.* m_pGet)();
		return true;
	}
protected:
	T (S::*	m_pGet)() const;
};

template <typename S, typename T>
class SetPropertyValueFunction : public WriteAccess<T>, public PropertyAccess<S> {
public:
	SetPropertyValueFunction(BasicProperty<S> & owner, void (S::* ptr)(T))
		: PropertyAccess<S>(owner), m_pSet(ptr) { }
public:
	virtual void SetValue(EditableObject & obj, const T & value) const {
		if (m_pSet == 0)
			return;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return;
		(tmp.* m_pSet)(value);
		m_Owner.SetValue(obj, tmp);
	}
protected:
	void (S::*	m_pSet)(T);
};

template <typename S, typename T>
class GetPropertyRefFunction : public ReadAccess<T>, public PropertyAccess<S> {
public:
	GetPropertyRefFunction(BasicProperty<S> & owner, const T & (S::* ptr)() const)
		: PropertyAccess<S>(owner), m_pGet(ptr) { }
public:
	virtual bool GetValue(const EditableObject & obj, T & value) const {
		if (m_pGet == 0)
			return false;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return false;
		value = (tmp.* m_pGet)();
		return true;
	}
protected:
	const T & (S::*	m_pGet)() const;
};

template <typename S, typename T>
class SetPropertyRefFunction : public WriteAccess<T>, public PropertyAccess<S> {
public:
	SetPropertyRefFunction(BasicProperty<S> & owner, void (S::* ptr)(const T &))
		: PropertyAccess<S>(owner), m_pSet(ptr) { }
public:
	virtual void SetValue(EditableObject & obj, const T & value) const {
		if (m_pSet == 0)
			return;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return;
		(tmp.* m_pSet)(value);
		m_Owner.SetValue(obj, tmp);
	}
protected:
	void (S::*	m_pSet)(const T &);
};

template <typename S, typename T>
class PropertyValue : public ReadWriteAccess<T>, public PropertyAccess<S> {
public:
	PropertyValue(BasicProperty<S> & owner, T (S::* ptr))
		: PropertyAccess<S>(owner), m_pValue(ptr) { }
public:
	virtual bool GetValue(const EditableObject & obj, T & value) const {
		if (m_pValue == 0)
			return false;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return false;
		value = tmp.* m_pValue;
		return true;
	}
	virtual void SetValue(EditableObject & obj, const T & value) const {
		if (m_pValue == 0)
			return;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return;
		tmp.* m_pValue = value;
		m_Owner.SetValue(obj, tmp);
	}
	virtual bool ReadOnly() const { 
		return false; 
	}
protected:
	T	(S::* m_pValue);
};

/// Casts property value to specified type U on use
template <typename S, typename T, typename U>
class PropertyValueAs : public ReadWriteAccess<U>, public PropertyAccess<S> {
public:
	PropertyValueAs(BasicProperty<S> & owner, T (S::* ptr))
		: PropertyAccess<S>(owner), m_pValue(ptr) { }
public:
	virtual bool GetValue(const EditableObject & obj, U & value) const {
		if (m_pValue == 0)
			return false;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return false;
		value = U(tmp.* m_pValue);
		return true;
	}
	virtual void SetValue(EditableObject & obj, const U & value) const {
		if (m_pValue == 0)
			return;
		S tmp;
		if (!m_Owner.GetValue(obj, tmp))
			return;
		tmp.* m_pValue = value;
		m_Owner.SetValue(obj, tmp);
	}
	virtual bool ReadOnly() const { 
		return false; 
	}
protected:
	T	(S::* m_pValue);
};

// object access generators

template <typename T, typename C>
inline boost::shared_ptr< ReadAccess<T> > Read(T (C::* ptr)() const)
	{ return boost::shared_ptr< ReadAccess<T> >(new GetObjectValueFunction<C, T>(ptr)); }

template <typename T, typename C>
inline boost::shared_ptr< WriteAccess<T> > Write(void (C::* ptr)(T))
	{ return boost::shared_ptr< WriteAccess<T> >(new SetObjectValueFunction<C, T>(ptr)); }

template <typename T, typename C>
inline boost::shared_ptr< ReadAccess<T> > Read(const T & (C::* ptr)() const)
	{ return boost::shared_ptr< ReadAccess<T> >(new GetObjectRefFunction<C, T>(ptr)); }

template <typename T, typename C>
inline boost::shared_ptr< WriteAccess<T> > Write(void (C::* ptr)(const T &))
	{ return boost::shared_ptr< WriteAccess<T> >(new SetObjectRefFunction<C, T>(ptr)); }

template <typename T, typename C>
inline boost::shared_ptr< ReadAccess<T> > Read(T C::* ptr)
	{ return boost::shared_ptr< ReadAccess<T> >(new ObjectValue<C, T>(ptr)); }

template <typename T, typename C>
inline boost::shared_ptr< WriteAccess<T> > Write(T C::* ptr)
	{ return boost::shared_ptr< WriteAccess<T> >(new ObjectValue<C, T>(ptr)); }

template <typename T, typename C>
inline boost::shared_ptr< ReadWriteAccess<T> > ReadWrite(T C::* ptr)
	{ return boost::shared_ptr< ReadWriteAccess<T> >(new ObjectValue<C, T>(ptr)); }

// property access generators

template <typename T, typename S>
inline boost::shared_ptr< ReadAccess<T> > Read(BasicProperty<S> & owner, T (S::* ptr)() const)
	{ return boost::shared_ptr< ReadAccess<T> >(new GetPropertyValueFunction<S, T>(owner, ptr)); }

template <typename T, typename S>
inline boost::shared_ptr< WriteAccess<T> > Write(BasicProperty<S> & owner, void (S::* ptr)(T))
	{ return boost::shared_ptr< WriteAccess<T> >(new SetPropertyValueFunction<S, T>(owner, ptr)); }

template <typename T, typename S>
inline boost::shared_ptr< ReadAccess<T> > Read(BasicProperty<S> & owner, const T & (S::* ptr)() const)
	{ return boost::shared_ptr< ReadAccess<T> >(new GetPropertyRefFunction<S, T>(owner, ptr)); }

template <typename T, typename S>
inline boost::shared_ptr< WriteAccess<T> > Write(BasicProperty<S> & owner, void (S::* ptr)(const T &))
	{ return boost::shared_ptr< WriteAccess<T> >(new SetPropertyRefFunction<S, T>(owner, ptr)); }

template <typename T, typename S>
inline boost::shared_ptr< ReadAccess<T> > Read(BasicProperty<S> & owner, T S::* ptr)
	{ return boost::shared_ptr< ReadAccess<T> >(new PropertyValue<S, T>(owner, ptr)); }

template <typename T, typename S>
inline boost::shared_ptr< WriteAccess<T> > Write(BasicProperty<S> & owner, T S::* ptr)
	{ return boost::shared_ptr< WriteAccess<T> >(new PropertyValue<S, T>(owner, ptr)); }

template <typename T, typename S>
inline boost::shared_ptr< ReadWriteAccess<T> > ReadWrite(BasicProperty<S> & owner, T S::* ptr)
	{ return boost::shared_ptr< ReadWriteAccess<T> >(new PropertyValue<S, T>(owner, ptr)); }

template <typename T, typename R, typename W>
inline boost::shared_ptr< ReadWriteAccess<T> > ReadWrite(R r, W w)
	{ return boost::shared_ptr< ReadWriteAccess<T> >(new ReadWriteImpl<T>(Read<T>(r), Write<T>(w))); }

template <typename T, typename S, typename R, typename W>
inline boost::shared_ptr< ReadWriteAccess<T> > ReadWrite(BasicProperty<S> & p, R r, W w)
	{ return boost::shared_ptr< ReadWriteAccess<T> >(new ReadWriteImpl<T>(Read<T>(p, r), Write<T>(p, w))); }

template <typename U, typename S, typename T>
inline boost::shared_ptr< ReadWriteAccess<U> > ReadWriteAs(BasicProperty<S> & owner, T S::* ptr)
	{ return boost::shared_ptr< ReadWriteAccess<U> >(new PropertyValueAs<S, T, U>(owner, ptr)); }

} // My

#include "Impl/ObjectProperty.inl"
